#

| Chapter 45 Shell Programming for the Initiated |

## 45.23 The Ins and Outs of Redirected I/O Loops

The Bourne shell usually runs a loop with redirected input or output (45.22) in a subshell (38.4). For the formprog script in article 45.22, this means, among other things, that:

- Any command inside the loop that reads its standard input will read from the pipe or file redirected to the loop's standard input. That's something you have to pay attention to, because the only command that should read from the file is usually the read command at the top of the loop. The inputs of other commands inside the loop - like commands that read from the terminal - have to be redirected to read from somewhere other than the loop's standard input.

- In many Bourne shells, if you use the exit (38.4) command inside a redirected loop, that will only terminate the subshell that's running the loop; it will not terminate the script. It's hard to call this a "feature"; I'd call it a bug. The script in article 45.22 has a workaround for this; see the next paragraph. Later versions of Bourne-like shells have fixed this problem, more or less, but the fix below should work in all Bourne shells.

- If there's any error inside the loop that should terminate the script, an error message is written to file descriptor 2. File descriptor 2 is redirected to an error-holding file at the subshell (loop) output. A break command can end the loop right away. After the loop ends, if the error file has anything in it, that means there was an error - if there are more commands to run, the script can terminate before running them.

- You can test the exit status (44.7) of the redirected-I/O loop. To end the loop, use a command like `exit 0`, `exit 2`, and so on. Just after the `done` command outside the loop, use `case $?` (44.5) to test the loop's status. For instance, a 0 status might mean the loop worked fine, a 1 could signal one kind of error, a 2 status a different error, and so on.

- If you change the value of any shell or environment variables inside the loop, their values outside the loop (after the done command at the end of the loop) will not be changed. Here's the usual fix for that problem. You use another file descriptor, like file descriptor 6, and write variable-setting commands to it. You redirect that file descriptor to a temporary file. Then, use the shell's dot command (`.`) (44.23) to read the temporary file into the shell outside the loop. For example, to get the value of a variable named varname outside the loop:

  >     while whatever
  >     do  ...
  >        echo "varname='value'" 1>&6
  >         ...
  >     done 6> var_set_file
  >     . var_set_file

Greg Ubben sent me two other ways that he prefers. The first one depends on having a read that accepts redirection on its command line, which most do these days. The second works when you can put the usage in the same scope (within the curly braces (13.8)) as the redirection:

>     exec 3< file   {
>     while read line <&3       while read line
>     do       do
>          var=value            var=value
>     done       done
>     exec 3<&-       echo "var = $var"
>     echo "var = $var"   } < file

Putting the loop inside a function and redirecting into the function also seems to avoid the subshell problem. But don't take my (our) word for it: test it on the shell you'll be using.

- JP

45.22 Handling Files Line-by-Line |  | 45.24 A Shell Can Read a Script from its Standard Input, But...